package show.me.the.money.config.reportSchema.define;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.dom4j.Document;
import org.dom4j.Element;

import show.me.the.money.config.DocConfig;
import show.me.the.money.config.SortComparator;
import show.me.the.money.config.Sortable;
import show.me.the.money.config.reportSchema.ReportDefine;
import show.me.the.money.config.reportSchema.ReportDefineFactory;
import show.me.the.money.config.schema.SchemaItem;
import show.me.the.money.config.schema.TypeValueUtil;
import show.me.the.money.util.ConfigValueParser;
import show.me.the.money.util.context.Context;


@SuppressWarnings("unchecked")
public class ReportSchema extends DocConfig{
	private static Log logger = LogFactory.getLog(ReportSchema.class);
	protected static ObjectMapper jsonMapper = new ObjectMapper();
	
	protected String title;
	protected List<HashMap<String, Object>> headers;
	protected List<ReportDefine> defines = new ArrayList<ReportDefine>();
	protected HashMap<String, String> diggers;
	protected HashMap<String, String> chartInfo;
	
	protected List cnds;
	protected Context ctx;
	protected List<SchemaItem> queryArgs = new ArrayList<SchemaItem>();

	public void setDefineDoc(Document doc,Context ctx){
		this.ctx=ctx;
		List recieveCnd=null;
		Object tempcnd=ctx.get("q.cnd");
		if(tempcnd instanceof List){
			recieveCnd=(List)tempcnd;
		}else if(tempcnd instanceof String){
			try {
				cnds = jsonMapper.readValue((String)tempcnd, List.class);
			} 
			catch (Exception e) {
				logger.error("parse reportCondition failed:", e);
			}
		}
		
		if(recieveCnd!=null){
			this.cnds=recieveCnd;
		}
		this.setDefineDoc(doc);
	};
	
	public void setDefineDoc(Document doc) {
		Element root = doc.getRootElement();
		title = root.attributeValue("title", "");

		Element cnd = root.element("condition");
		if (cnd != null) {
			try {
				if(cnds == null){
					cnds = jsonMapper.readValue(cnd.getText().replaceAll("'", "\""),List.class);
				}
				else{
					if(cnds.get(0).equals("and")){
						cnds.add(jsonMapper.readValue(cnd.getText().replaceAll("'", "\""),List.class));
					}
					else{
						List o = new ArrayList();
						o.add("and");
						o.add(cnds);
						o.add(jsonMapper.readValue(cnd.getText().replaceAll("'", "\""),List.class));
						cnds = o;
					}
				}
			}catch (Exception e) {
				logger.error("parse reportCondition failed:", e);
			}
		}
		List<Element> els = root.selectNodes("queryArgs/arg");
		int i = 0;
		for (Element el : els) {
			SchemaItem si = new SchemaItem(el, i);
			queryArgs.add(si);
			i++;
		}

		els = root.elements("define");
		int startRenderIndex = 0;
		for (Element el : els) {
			ReportDefine define = ReportDefineFactory.createInstance(el,
					startRenderIndex);
			define.setReportSchema(this);
			define.setQueryArgs(queryArgs);
			startRenderIndex += define.getHeaderCount();
			if (cnds != null) {
				define.addCondition(cnds);
			}
			defines.add(define);
		}

		Element el = root.element("digger");
		if (el != null) {
			diggers = new HashMap<String, String>();
			els = el.elements();
			for (Element dig : els) {
				diggers.put(dig.attributeValue("match"), dig
						.attributeValue("target"));
			}
		}

		el = root.element("chart");
		if (el != null) {
			chartInfo = new HashMap<String, String>();
			chartInfo.put("template", el.attributeValue("template"));
			chartInfo.put("chartType", el.attributeValue("chartType"));
			chartInfo.put("limit", el.attributeValue("limit"));
		}

	}

	public String getTitle() {
		return title;
	}

	public List<HashMap<String, Object>> getHeaders() {
		if (headers == null) {
			List<SchemaItem> ls = new ArrayList<SchemaItem>();
			int i = 0;
			for (ReportDefine define : defines) {
				if (i == 0) {
					ls = define.getHeaders(true);
				} else {
					ls.addAll(define.getHeaders(false));
				}
				i++;
			}
			Comparator<Sortable> c = new SortComparator();
			Collections.sort(ls, c);

			headers = new ArrayList<HashMap<String, Object>>();
			for (SchemaItem it : ls) {
				headers.add(it.data());
			}
		}
		return headers;
	}

	public HashMap<String, Object> createEmptyRecord() {
		HashMap<String, Object> r = new HashMap<String, Object>();
		List<HashMap<String, Object>> headers = getHeaders();

		for (HashMap<String, Object> h : headers) {
			int type = TypeValueUtil.getStaticType((String)h.get("type"));
			String id = (String)h.get("id");
			if (type < 7) {
				r.put(id, 0);
			} else {
				r.put(id, "");
			}
		}
		return r;
	}

	public List<HashMap> run(Context ctx){
		List<HashMap> data = null;
		if (defines.size() == 1) {
			data = defines.get(0).runSingleMode(ctx);
		} else {
			HashMap<String, HashMap> container = new LinkedHashMap<String, HashMap>();
			for (ReportDefine define : defines) {
				define.runMutiMode(container, ctx);
			}
			data = new ArrayList<HashMap>();
			data.addAll(container.values());
			ctx.put("totalCount", data.size());
			ctx.put("pageSize", data.size());
			ctx.put("pageNo", 1);
		}
//		System.out.println(data);
		return data;
	}

	public String getSubTitle(Context ctx) {
		StringBuffer subCaption = new StringBuffer();
		for (SchemaItem a : queryArgs) {
			String argName = "q." + a.getId();
			if(a.getProperty("hidden")!=null) continue; 
			subCaption.append(a.getProperty("alias")).append(":");
			if (a.isCodedValue()) {
				subCaption.append(a.getDisplayValue(ctx.value(argName)));
			} else {
				subCaption.append(ctx.value(argName));
			}
			subCaption.append(" ");
		}
		return subCaption.toString();
	}

	public ReportDefine getReportDefine() {
		return getReportDefine(0);
	}

	public ReportDefine getReportDefine(int i) {
		if (i >= defines.size()) {
			return null;
		}
		return defines.get(i);
	}

	public HashMap<String, String> getDiggers() {
		return diggers;
	}

	public String getChartTemplate() {
		return chartInfo.get("template");
	}

	public int getChartLimit() {
		Object v = chartInfo.get("limit");
		if (v != null) {
			return Integer.valueOf((String) v);
		} else {
			return 0;
		}
	}

	public HashMap<String,Object> json(Context ctx) {
		HashMap<String,Object> data = new HashMap<String,Object>();

		List<HashMap<String, Object>> args = new ArrayList<HashMap<String, Object>>();
		try {
			List<HashMap<String, Object>> list = getHeaders();
			List headers = new ArrayList();
			for (HashMap<String, Object> h : list) {
				headers.add(h);
			}
			data.put("title", title);
			data.put("items", headers);
			data.put("args", args);
			for (SchemaItem arg : queryArgs) {
				HashMap<String,Object> o = arg.data();
				if (o.containsKey("defaultValue")) {
					String v = ConfigValueParser.parse(arg.getDefaultValue(),
							ctx);
					if (arg.isCodedValue()) {
						HashMap<String, Object> cv = new HashMap<String, Object>();
						cv.put("key", v);
						cv.put("text", arg.getDisplayValue(v));
						o.put("defaultValue", cv);
					} else {
						o.put("defaultValue", v);
					}
				}
				if (o.containsKey("dic")) {
					HashMap<String, Object> dic = (HashMap<String, Object>)o.get("dic");
					if (dic.containsKey("parentKey")) {
						String v = ConfigValueParser.parse((String)dic.get("parentKey"), ctx);
						dic.put("parentKey", v);
					}
				}
				args.add(o);
			}
			data.put("diggers", diggers);
			data.put("chart", chartInfo);
		} 
		catch (Exception e) {

		}
		return data;
	}
}
